
import 'package:flutter/material.dart';

enum ModalStates { translation, scale }

class ModalWidget extends StatefulWidget {
  ModalWidget(
      {Key? key,
      required this.child,
      this.duration = const Duration(milliseconds: 250),
      this.curve = Curves.easeIn,
      this.alignment = Alignment.center,
      this.backgroundColor,
      this.modalState = ModalStates.translation,
      this.touchHiden = true})
      : super(key: key);

  final Widget child;
  Duration? duration;
  final Curve curve;
  final AlignmentGeometry alignment;
  Color? backgroundColor;
  final ModalStates modalState;
  bool touchHiden;
  @override
  State<StatefulWidget> createState() => _ModalWidgetState();

  ///
  /// child 需要使用builder 获取上下文 才能在子组件中调用dismiss方法 隐藏组件
  static Future<T?> showModal<T extends Object?>(
          BuildContext context, Widget child,
          {AlignmentGeometry alignment = Alignment.center,
          ModalStates modalState = ModalStates.scale,
          Curve curve = Curves.bounceIn,
          bool touchHidden = false}) =>
      Navigator.of(context, rootNavigator: true).push(PageRouteBuilder(
          transitionDuration: const Duration(milliseconds: 150),
          reverseTransitionDuration: const Duration(milliseconds: 100),
          pageBuilder: (context, animation, secondaryAnimation) =>
              FadeTransition(
                opacity: animation,
                child: ModalWidget(
                  alignment: alignment,
                  modalState: modalState,
                  curve: curve,
                  touchHiden: touchHidden,
                  child: child,
                ),
              ),
          opaque: false));

  /// 需要通过builder 创建child widget 才能通过此方法获取最近父节点的state
  static void dismiss<T extends Object?>(BuildContext context,
      {bool animation = true, T? result}) {
    _ModalWidgetState? widget =
        context.findAncestorStateOfType<_ModalWidgetState>();
    widget?.dismiss(animation: animation, result: result);
  }
}

class _ModalWidgetState extends State<ModalWidget>
    with SingleTickerProviderStateMixin {
  late AnimationController showAnimationControl;
  late Animation<Offset> positionAnimation;
  late Animation<double> scaleAnimation;

  Function()? afterHiden;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    showAnimationControl =
        AnimationController(vsync: this, duration: widget.duration);
    showAnimationControl.addStatusListener((status) {
      if (status == AnimationStatus.reverse && afterHiden != null) {
        afterHiden!();
      }
    });

    final CurvedAnimation curvedAnimation =
        CurvedAnimation(parent: showAnimationControl, curve: widget.curve);

    if (widget.modalState == ModalStates.translation) {
      positionAnimation =
          Tween(begin: const Offset(0, 1), end: const Offset(0, 0))
              .animate(curvedAnimation);
    } else {
      scaleAnimation = Tween(begin: 0.8, end: 1.0).animate(curvedAnimation);
    }
    showAnimationControl.forward();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return WillPopScope(
        child: Material(
          color: Colors.transparent,
          child: GestureDetector(
            onTap: widget.touchHiden ? () => dismiss() : null,
            child: Container(
              color: widget.backgroundColor ?? Colors.black12.withAlpha(88),
              child: Align(
                alignment: widget.alignment,
                child: _buildWidget(),
              ),
            ),
          ),
        ),
        onWillPop: () async {
          return false;
        });
  }

  Widget _buildWidget() {
    if (widget.modalState == ModalStates.scale) {
      return ScaleTransition(
        scale: scaleAnimation,
        child: widget.child,
      );
    }
    return SlideTransition(
      position: positionAnimation,
      child: widget.child,
    );
  }

  void dismiss<T extends Object?>({bool animation = true, T? result}) {
    if (animation) {
      afterHiden = () {
        Navigator.of(context).pop(result);
      };
      showAnimationControl.reverse();
    } else {
      Navigator.of(context).pop(result);
    }
  }

  @override
  void dispose() {
    // TODO: implement dispose
    showAnimationControl.dispose();

    super.dispose();
  }
}
